using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Text;


namespace Ultima {
  public class Hues {
    private static readonly Hue[] m_List;

    static Hues() {
      string path = Client.GetFilePath( "hues.mul" );
      int index = 0;

      m_List = new Hue[3000];

      if ( path != null ) {
        using ( var fs = new FileStream( path, FileMode.Open, FileAccess.Read, FileShare.Read ) ) {
          var bin = new BinaryReader( fs );

          int blockCount = (int)fs.Length / 708;

          if ( blockCount > 375 )
            blockCount = 375;

          for ( int i = 0; i < blockCount; ++i ) {
            bin.ReadInt32();

            for ( int j = 0; j < 8; ++j, ++index )
              m_List[index] = new Hue( index, bin );
          }
        }
      }

      for ( ; index < 3000; ++index )
        m_List[index] = new Hue( index );
    }

    public static Hue[] List {
      get { return m_List; }
    }

    public static Hue GetHue( int index ) {
      index &= 0x3FFF;

      if ( index >= 0 && index < 3000 )
        return m_List[index];

      return m_List[0];
    }
  }


  public class Hue {
    private readonly short[] m_Colors;
    private readonly int m_Index;
    private readonly string m_Name;

    public Hue( int index ) {
      this.m_Name = "Null";
      this.m_Index = index;
      this.m_Colors = new short[34];
    }

    public Hue( int index, BinaryReader bin ) {
      this.m_Index = index;
      this.m_Colors = new short[34];

      for ( int i = 0; i < 34; ++i )
        this.m_Colors[i] = (short)( bin.ReadUInt16() | 0x8000 );

      bool nulled = false;

      var sb = new StringBuilder( 20, 20 );

      for ( int i = 0; i < 20; ++i ) {
        var c = (char)bin.ReadByte();

        if ( c == 0 )
          nulled = true;
        else if ( !nulled )
          sb.Append( c );
      }

      this.m_Name = sb.ToString();
    }

    public int Index {
      get { return this.m_Index; }
    }
    public short[] Colors {
      get { return this.m_Colors; }
    }
    public string Name {
      get { return this.m_Name; }
    }

    public Color GetColor( int index ) {
      int c16 = this.m_Colors[index];

      return Color.FromArgb( ( c16 & 0x7C00 ) >> 7, ( c16 & 0x3E0 ) >> 2, ( c16 & 0x1F ) << 3 );
    }

    public unsafe void ApplyTo( Bitmap bmp, bool onlyHueGrayPixels ) {
      BitmapData bd = bmp.LockBits( new Rectangle( 0, 0, bmp.Width, bmp.Height ), ImageLockMode.ReadWrite,
                                   PixelFormat.Format16bppArgb1555 );

      int stride = bd.Stride >> 1;
      int width = bd.Width;
      int height = bd.Height;
      int delta = stride - width;

      var pBuffer = (ushort*)bd.Scan0;
      ushort* pLineEnd = pBuffer + width;
      ushort* pImageEnd = pBuffer + ( stride * height );

      ushort* pColors = stackalloc ushort[0x40];

      fixed ( short* pOriginal = this.m_Colors ) {
        var pSource = (ushort*)pOriginal;
        ushort* pDest = pColors;
        ushort* pEnd = pDest + 32;

        while ( pDest < pEnd )
          *pDest++ = 0;

        pEnd += 32;

        while ( pDest < pEnd )
          *pDest++ = *pSource++;
      }

      if ( onlyHueGrayPixels ) {
        int c;
        int r;
        int g;
        int b;

        while ( pBuffer < pImageEnd ) {
          while ( pBuffer < pLineEnd ) {
            c = *pBuffer;
            r = ( c >> 10 ) & 0x1F;
            g = ( c >> 5 ) & 0x1F;
            b = c & 0x1F;

            if ( r == g && r == b )
              *pBuffer++ = pColors[c >> 10];
            else
              ++pBuffer;
          }

          pBuffer += delta;
          pLineEnd += stride;
        }
      } else {
        while ( pBuffer < pImageEnd ) {
          while ( pBuffer < pLineEnd ) {
            *pBuffer = pColors[( *pBuffer ) >> 10];
            ++pBuffer;
          }

          pBuffer += delta;
          pLineEnd += stride;
        }
      }

      bmp.UnlockBits( bd );
    }
  }
}